package yapl.symbolChecker;


import java.util.Vector;

import yapl.lib.CompilerMessage;
import yapl.parser.AdvancedParserOld;

public class Main extends CompilerMessage {
	public static Vector<String> tokens=new Vector<String>();
	public static int location=0;
	public static SymbolTable symbolTable=new SymbolTable();
	public static String programName;
	public static boolean isInsideProcedure;
	public static Vector<String> classes=new Vector<String>();
	public static Vector<int[]> lineInfo=new Vector<int[]>();
	public static boolean shouldOpenScope;
	public static String lastProcedureName;
	public static String inputTestFileName;
	
	public static void main(String[] args){
		//inputTestFileName=args[0];
		inputTestFileName="test02.yapl";
		try {
			start();
		} catch(SymbolException e) {
			printError(e,programName);
		}
	}

	public static void start() throws SymbolException{
		AdvancedParserOld.testFile=inputTestFileName;
		AdvancedParserOld.start();
		tokens=AdvancedParserOld.tokens;
		
		//System.out.println(tokens);
		//Start------------------------
		Symbol program=new Symbol(tokens.elementAt(1),1);
		programName=tokens.elementAt(1);
		symbolTable.scopes.elementAt(0).put(programName,new Symbol(1));
		location=2;
		symbolTable.openScope();
		shouldOpenScope=false;

		while(location<tokens.size()) {
			//System.out.println(currentToken());
			if(currentToken().equals("Declare")) declare();
			else if(currentToken().equals("Procedure")) procedure();
			else if(currentToken().equals("Begin")) begin();
			else if(currentToken().equals("End")) end();
		}
			
			

		
		
	}

	 public static String currentToken(){
		 return tokens.elementAt(location);
	 }
	 public static String nextToken(){
		 return tokens.elementAt(location+1);
	 }
	 public static String previousToken(){
		 return tokens.elementAt(location-1);
	 }
	 public static String lookup() {
		 return symbolTable.lookup(currentToken());
	 }
	 
	 public static void declare() throws SymbolException{
		 if(shouldOpenScope==true) symbolTable.openScope();
		 location++;
		 while(currentToken().equals("Procedure")==false&&currentToken().equals("Begin")==false&&currentToken().equals("End")==false){
			 if(currentToken().equals("Const")) {
				 location++;
				 symbolTable.addSymbol(new Symbol(currentToken(), 4));
				 //Symbol symbol = new Symbol(currentToken(), 4);
//				 location = location + 2;
//				 if (isInteger(currentToken())) {
//					 Symbol symbol = new Symbol(currentToken(), 4, new Type());
//				 }
				 location=find(";")+1;				 
			 }
			 else if(currentToken().equals("Record")) record();
			 else{//if currentToken() is a type name
				 if(currentToken().equals("int")==false&&currentToken().equals("bool")==false&&
						 (symbolTable.lookup(currentToken())=="typename")==false){
					 error(11);
				 }
				 skipArrayBrackets();
				 symbolTable.addSymbol(new Symbol(currentToken(),3));
				 
				 location++;
				 if(currentToken().equals(";")==true) location++;
				 else if(currentToken().equals(",")){
					 while(currentToken().equals(";")==false) {//if more than one variable is being declared in one line
						 if(currentToken().equals(",")){
							 location++;
						 }
						 else{
							 symbolTable.addSymbol(new Symbol(currentToken(),3));
							 location++;
						 }
					 }
					 location++;
				 }
			 }
		 }
	 }
	 
	 public static void procedure() throws SymbolException{
		 isInsideProcedure=true;
		 location++;
		 skipArrayBrackets();;
		 symbolTable.addSymbol(new Symbol(currentToken(),2));//add name of procedure to ST
		 lastProcedureName=currentToken();
		 location=location+2;
		 symbolTable.openScope();
		 while(currentToken().equals(")")==false){
			 if(currentToken().equals(",")){
				 symbolTable.addSymbol(new Symbol(previousToken(),7));//add parameter names to ST
				 location++;
			 }
			 else{
				 location++;
			 }
		 
		 }
		 location--;
		 symbolTable.addSymbol(new Symbol(currentToken(),7));//add last parameter name to ST
		 location=location+2;
		 if(currentToken().equals("Declare")) declare();
		 begin();
		 endProcedure();
		 symbolTable.closeScope();
		 isInsideProcedure=false;
	 }
	 
	 public static void begin() throws SymbolException{
		 location++;
		 while(currentToken().equals("End")==false){
			 
			 if(currentToken().equals("(")&&previousToken().equals("If")==false){
				 checkProcedureCall();
			 }
			 
			 else if(isOperator(currentToken())||isOther(currentToken())){
				 location++;
			 }
			 else if(currentToken().equals("#")) checkArrayLen();
			 else if(currentToken().equals("Write")) {
				 location=location+3;
			 }
			 else if(isInteger(currentToken())){
				 location++;
			 }
			 else if(currentToken().equals("Declare")){
				 declare();
				 begin();
				 endBlock();
			 }
			 else if(currentToken().equals(".")){//skip symbol checking for "field" types
				 location=location+2;
			 }
			 else{//if token is an identifier
				
				 if(currentToken().equals(":=")){
					 checkAssignment();
				 }
				 else{
					 if(symbolTable.lookup(currentToken())=="procedure"&&nextToken().equals("(")==false){
						 error(12);
					 }
					 else if(symbolTable.isInSymbolTable(currentToken())) {
						 location++;
					 } 
					 else{
						 error(11);
					 }
				 }
				
			 }
			 
		 }
		 
	 }
	 
	 public static void endBlock() {
		 symbolTable.closeScope();
		 location=location+2;
	 }
	 
	 public static void endProcedure() throws SymbolException {
		 isInsideProcedure=false;
		 location++;
		 if(currentToken().equals(lastProcedureName)) {
			 location=location+2;
		 }
		 else{
			 SymbolException e=new SymbolException(lineInfo.elementAt(location)[0],lineInfo.elementAt(location)[1],13);
			 e.isProcedure=true;
			 throw e;
		 }
	 }
	 
	 public static void end() throws SymbolException{
		 symbolTable.closeScope();
		 location++;
		 if(currentToken().equals(programName)){
			 location=location+2;
			 printOK(programName);
			 //System.out.println("End of Program reached");
		 }
		 else{
			 error(13);
		 }
	 }
	 
	 public static void record() throws SymbolException{
		 //System.out.println(currentToken());
		 location++;
		 symbolTable.addSymbol(new Symbol(currentToken(),5));
		 symbolTable.openScope();
		 location++;
		 while(currentToken().equals("EndRecord")==false){
			 if(currentToken().equals("Const")){
				 location++;
				 symbolTable.addSymbol(new Symbol(currentToken(),6));
				 location=find(";")+1;
			 }
			 else{//if currentToken() is a type name
				 if(currentToken().equals("int")==false&&currentToken().equals("bool")==false&&
						 (symbolTable.lookup(currentToken())=="typename")==false){
					 error(11);
				 }
				 skipArrayBrackets();
				 symbolTable.addSymbol(new Symbol(currentToken(),6));
				 
				 location++;
				 if(currentToken().equals(";")==true) location++;
				 else if(currentToken().equals(",")){
					 while(currentToken().equals(";")==false) {//if more than one variable is being declared in one line
						 if(currentToken().equals(",")){
							 location++;
						 }
						 else{
							 location++;
						 }
					 }
					 location++;
				 }
			 }
		 }
		 symbolTable.closeScope();
		 location=location+2;
	 }
	 
	 
	 
	 public static void checkAssignment() throws SymbolException{
		 int tempLocation = location;
		 while(symbolTable.isInSymbolTable(currentToken())==false){
			 location--;
		 }
		 while(currentToken().equals(":=")==false) {
			 if(isOperator(currentToken())||isOther(currentToken())||isInteger(currentToken())) {
				 location++;
			 }
			 else if(currentToken().equals(".")){
				 location=location+2;
			 }
			 else if(symbolTable.lookup(currentToken()).equals("procedure")==true){
				 while(currentToken().equals(")")==false){
					 location++;
				 }
			 }
			 else {
				 if(symbolTable.lookup(currentToken()).equals("variable")==false&&symbolTable.lookup(currentToken()).equals("parameter")==false){
					 error(12);
				 }
				 location++;
			 }
		 }
		 location++;
		 while(currentToken().equals(";")==false) {
			 if(currentToken().equals("#")) checkArrayLen();
//			 else if (currentToken().equals("-") || currentToken().equals("+") && nextToken()) {
//				 
//				 
//			 }
			 else if(isOperator(currentToken())||isOther(currentToken())||isInteger(currentToken())) {
				 location++;
			 }
			 else if(currentToken().equals(".")){
				 location=location+2;
			 }
			 else if(symbolTable.lookup(currentToken()).equals("procedure")==true){
				 while(currentToken().equals(")")==false){
					 location++;
				 }
			 }
			 else {
				 if(symbolTable.lookup(currentToken()).equals("program")==true||symbolTable.lookup(currentToken()).equals("field")==true){
					 error(12);
				 }
				 else if(symbolTable.lookup(currentToken())=="procedure"&&nextToken().equals("(")==false){
					 error(12);
				 }
				 location++;
			 }
		 }
		 location=tempLocation;
		 location++;
	 }
	 
	 public static void checkArrayLen() throws SymbolException{
		 location++;
		 if(symbolTable.isInSymbolTable(currentToken())==false) error(11);
		 if(symbolTable.lookup(currentToken()).equals("variable")==false&&symbolTable.lookup(currentToken()).equals("parameter")==false){
			 error(12);
		 }
	 }
	 
	 public static void checkProcedureCall() throws SymbolException {
		 location--;
		 if(symbolTable.lookup(currentToken()).equals("procedure")==false){
			 error(12);
		 }
		 location=location+2;
//		 while(currentToken().equals(")")==false){
//			 if(currentToken().equals(",")) location++;
//			 else{
//				 if(symbolTable.lookup(currentToken()).equals("variable")==false&&symbolTable.lookup(currentToken()).equals("parameter")==false
//						 &&symbolTable.lookup(currentToken()).equals("typename")==false){
//					 throw new SymbolException(lineInfo.elementAt(location)[0],lineInfo.elementAt(location)[1],12);
//				 }
//			 }
//		 }
//		 location++;
	 }
	 
	 public static void error(int errorNum) throws SymbolException{
		 throw new SymbolException(lineInfo.elementAt(location)[0],lineInfo.elementAt(location)[1],errorNum);
	 }
	 
	 
	 public static int find(String input){
		 int findlocation=location+1;
		 while(!(tokens.elementAt(findlocation).equals(input))) {
			 findlocation++;
		 }
		 return findlocation;
	 }
	 
	 public static void skipArrayBrackets(){
		 if(nextToken().equals("[")){
			 location++;
			 while(currentToken().equals("[")||currentToken().equals("]")) {
				 location++;
			 }
		 }
		 else{
			 location++;
			 }
	 }
	 
	 public static boolean isInteger( String input )  
	 {
	    try  
	    {  
	       Integer.parseInt( input );
	       return true;  
	    }  
	    catch(Exception e)  
	    {  
	       return false;  
	    }  
	 } 
	 
	 public static boolean isOperator(String t){
		 return (t.equals("<")||t.equals("<=")||t.equals(">=")||t.equals(">")||t.equals(".equals(")||
				 t.equals("!=")||t.equals("+")||t.equals("-")||t.equals("*")||t.equals("/")||t.equals("%"));
	 }
	 
	 public static boolean isOther(String t){
		 return (t.equals("new")||t.equals("int")||t.equals("bool")||t.equals(";")||t.equals(",")||
				 t.equals("If")||t.equals("EndIf")||t.equals("While")||t.equals("EndWhile")||t.equals("And")||t.equals("Do")||
				 t.equals("]")||t.equals("[")||t.equals("(")||t.equals(")")||t.equals("Return")||t.equals("Then"));	
	 }	 
	 
	 
//	 public boolean isIntegerType(String type) {
//		 if (type.equals("True") || type.equals("False")) {
//			 return false;
//		 } else if (symbolTable.getSymbol(type).getType() instanceof IntType) {
//			 
//			 
//		 }
//	 }
//	 
	 
	 
	 
	 
	 
	 
	 
	 
	 
	 
//	 public static boolean isPredefined(String t) {
//		 return (t.equals("writeint")||t.equals("writebool")||t.equals("writeln")||t.equals("readint"));
//		 
//	 }
	 
//	 public static boolean isType(String input) {
//		 if(input.equals("int")
//	 }
	 
	 


	
}
